package com.essence.grabiec104.grab.strategy;

import com.essence.common.model.EnumTypeAdapter;
import com.essence.common.model.Substation;
import com.essence.grabiec104.grab.dao.AlarmDao;
import com.essence.grabiec104.grab.model.AlarmRecord;
import com.essence.grabiec104.grab.model.AlarmRule;
import com.essence.grabiec104.grab.model.query.PointCountQuery;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@Component("sameYkPointStrategy")
@Slf4j
public class SameYkPointStrategy extends AbstractSocketAlarmStrategy {
    @Autowired
    private AlarmDao alarmDao;

    public static final int ALARM_SCAN_FLAG_BIT = 3;

    private Gson gson;

    public SameYkPointStrategy() {
        this.gson = new GsonBuilder()
                .registerTypeAdapterFactory(new TypeAdapterFactory() {
                    @Override
                    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
                        Class<? super T> rawType = type.getRawType();
                        if (!Enum.class.isAssignableFrom(rawType)) {
                            return null;
                        }
                        TypeAdapter<T> adapter = new EnumTypeAdapter();
                        return adapter;
                    }
                })
                .create();
    }

    @Override
    protected boolean doHandle(List<AlarmRule> rules) {
        // 过滤出遥控点告警类型的规则
        List<AlarmRule> rs = rules.stream()
                .filter(rule -> rule.getMetricTypeIndex() == AlarmRule.MetricTypeEnum.SAME_YK_POINT.ordinal())
                .collect(Collectors.toList());
        if (rs.isEmpty()) {
            return false;
        }

        // 获取需要扫描的站点列表
        List<Substation> substations = alarmDao.getAllSubstation().stream()
                .filter(sub -> sub.isRtu() && (sub.getScanFlag() >> ALARM_SCAN_FLAG_BIT & 1L) == 0L)
                .collect(Collectors.toList());
        if (substations.isEmpty()) {
            return false;
        }

        // 过滤出需要扫描的规则
        List<AlarmRule> scanRules = rs.stream()
                .filter(rule ->
                        substations.stream()
                                .map(Substation::getId)
                                .anyMatch(id -> id == rule.getSubstation().getId())
                )
                .collect(Collectors.toList());
        if (scanRules.isEmpty()) {
            return false;
        }

        // 4. 处理每个规则
        for (AlarmRule rule : scanRules) {
            long substationId = rule.getSubstation().getId();
            long startTime = System.currentTimeMillis() - rule.getIntervalSeconds() * 1000L;

            // 统计每个遥控点的操作次数
            List<PointCountQuery> pointOperations = alarmDao.countYkPointOperationsInTimeRange(substationId, startTime);
            List<AlarmRecord> existingAlarms = alarmDao.selectOpeningAlarmRecords(rule.getId(), substationId);
            if(pointOperations.isEmpty() && existingAlarms.isEmpty()) {
                Substation sub = alarmDao.getSubstationById(substationId);
                long scanFlag = sub.getScanFlag() | (1L << ALARM_SCAN_FLAG_BIT);
                alarmDao.updateSubstationScanFlag(substationId, scanFlag);
                log.info("告警类型: SAME_YK_POINT(多次对相同地址遥控) 关闭告警");
            }

            Set<Long> pointIds = pointOperations.stream().map(PointCountQuery::getId).collect(Collectors.toSet());
            List<AlarmRecord> needsToClosedAlarms = existingAlarms.stream().filter(alarm -> !pointIds.contains(alarm.getPointId())).collect(Collectors.toList());
            for (AlarmRecord alarm : needsToClosedAlarms) {
                alarm.setStatus(0);
                alarm.setSubstation(rule.getSubstation());
                alarm.setAlarmRule(rule);
                alarm.setUpdatedAt(System.currentTimeMillis());
                alarmDao.updateAlarmById(alarm);
                Substation sub = alarmDao.getSubstationById(substationId);
                long scanFlag = sub.getScanFlag() | (1L << ALARM_SCAN_FLAG_BIT);
                alarmDao.updateSubstationScanFlag(substationId, scanFlag);
                publish("append-alarm-record", List.of(alarm));
                log.info("告警类型: SAME_YK_POINT(多次对相同地址遥控) 关闭告警");
            }

            // 处理每个超过阈值的点
            for (PointCountQuery pointOp : pointOperations) {
                long pointId = pointOp.getId();
                int operationCount = pointOp.getCount();
                AlarmRecord existingAlarm = alarmDao.selectOpeningAlarmRecord(rule.getId(), substationId, pointId);
                if (operationCount > rule.getThreshold()) {
                    if (existingAlarm == null) {
                        // 4.4 创建新告警记录
                        AlarmRecord ar = new AlarmRecord();
                        ar.setAlarmRuleId(rule.getId());
                        ar.setStateId(substationId);
                        ar.setPointId(pointId);
                        ar.setPointAddress(pointOp.getPointAddress());
                        ar.setMaxThreshold(operationCount);
                        ar.setStatus(1);
                        ar.setSubstation(rule.getSubstation());
                        ar.setAlarmRule(rule);
                        ar.setCreatedAt(System.currentTimeMillis());
                        ar.setUpdatedAt(System.currentTimeMillis());
                        alarmDao.insertAlarm(ar);
                        publish("append-alarm-record", List.of(ar));
                        log.info("告警类型: SAME_YK_POINT(多次遥控相同地址) 新增告警");
                    } else if (existingAlarm.getStatus() == 1 && operationCount > existingAlarm.getMaxThreshold()) {
                        existingAlarm.setMaxThreshold(operationCount);
                        existingAlarm.setUpdatedAt(System.currentTimeMillis());
                        existingAlarm.setSubstation(rule.getSubstation());
                        existingAlarm.setAlarmRule(rule);
                        alarmDao.updateAlarmById(existingAlarm);
                        publish("append-alarm-record", List.of(existingAlarm));
                        log.info("告警类型: SAME_YK_POINT(多次遥控相同地址) 更新告警");
                    }
                }
            }
        }

        return true;
    }
}
